using System;
using System.Collections.Generic;
using System.Diagnostics;
using NUnit.Framework;

namespace nzmq.tests
{
    [TestFixture]
    public class use_NzmqClientSocket_and_NzmqServiceSocket
    {
        [Test]
        public void Send_string_from_client_to_service()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck = ctx.CreateServiceSocket("inproc://service"))
            using (var clientSck = ctx.CreateClientSocket("inproc://service"))
            {
                clientSck.Send("hello");
                Assert.AreEqual("hello", serviceSck.Receive());
            }
        }


        [Test]
        public void Send_object_from_client_to_service()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck = ctx.CreateServiceSocket("inproc://service"))
            using (var clientSck = ctx.CreateClientSocket("inproc://service"))
            {
                clientSck.Send(new Uri("http://nzmq.codeplex.com"));
                Assert.AreEqual(new Uri("http://nzmq.codeplex.com"), serviceSck.Receive());
            }
        }


        [Test]
        public void Reply_from_service_to_client()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck = ctx.CreateServiceSocket("inproc://service"))
            using (var clientSck = ctx.CreateClientSocket())
            {
                clientSck.Connect("inproc://service");

                clientSck.Send("hello");
                serviceSck.Receive();
                serviceSck.Reply("world");
                Assert.AreEqual("world", clientSck.Receive());
            }
        }


        [Test]
        public void Host_service()
        {
            using (var ctx = new NzmqContext())
            {
                ctx.HostService(sck => {
                                    Assert.AreEqual("the", sck.Receive());
                                    sck.Reply("quick");
                                    Assert.AreEqual("brown", sck.Receive());
                                    sck.Reply("fox");
                                },
                                "tcp://127.0.0.1:5000");

                using (var clientSck = ctx.CreateClientSocket("tcp://127.0.0.1:5000"))
                {
                    clientSck.Send("the");
                    Assert.AreEqual("quick", clientSck.Receive());
                    clientSck.Send("brown");
                    Assert.AreEqual("fox", clientSck.Receive());
                }
            }
        }


        [Test]
        public void Send_and_receive_multipart_msg()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck = ctx.CreateServiceSocket("inproc://service1"))
            using (var clientSck = ctx.CreateClientSocket("inproc://service1"))
            {
                clientSck.Send("hello", true);
                clientSck.Send("world", false);

                Assert.AreEqual("hello", serviceSck.Receive());
                Assert.IsFalse(serviceSck.EndOfMultipartMessage);
                Assert.AreEqual("world", serviceSck.Receive());
                Assert.IsTrue(serviceSck.EndOfMultipartMessage);
            }
        }


        [Test]
        public void Multipart_msg_gets_delivered_to_requesting_service_even_if_multiple_are_connected()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck1 = ctx.CreateServiceSocket("inproc://service1"))
            using (ctx.CreateServiceSocket("inproc://service2"))
            using (var clientSck = ctx.CreateClientSocket("inproc://service1"))
            {
                clientSck.Connect("inproc://service2");

                clientSck.Send("hello", true);
                clientSck.Send("world", false);

                Assert.AreEqual("hello", serviceSck1.Receive());
                Assert.IsFalse(serviceSck1.EndOfMultipartMessage);
                Assert.AreEqual("world", serviceSck1.Receive());
                Assert.IsTrue(serviceSck1.EndOfMultipartMessage);
            }
        }


        [Test]
        public void Wait_successfully_for_a_response()
        {
            using (var ctx = new NzmqContext())
            using (var serviceSck = ctx.CreateServiceSocket("inproc://service"))
            using (var clientSck = ctx.CreateClientSocket())
            {
                clientSck.Connect("inproc://service");

                clientSck.Send("hello");
                serviceSck.Receive();
                serviceSck.Reply("world");

                string text;
                Assert.IsTrue(clientSck.TryReceive(out text, 100));
                Assert.AreEqual("world", text);
            }
        }


        [Test]
        public void Wait_in_vain_for_a_response()
        {
            using (var ctx = new NzmqContext())
            using (ctx.CreateServiceSocket("inproc://service")) 
            using (var clientSck = ctx.CreateClientSocket())
            {
                clientSck.Connect("inproc://service");

                string text;
                Assert.IsFalse(clientSck.TryReceive(out text, 100));
            }
        }


        [Test]
        public void Wait_for_messages_on_several_sockets()
        {
            using (var ctx = new NzmqContext())
            using (var ssck1 = ctx.CreateServiceSocket("inproc://service1"))
            using (var ssck2 = ctx.CreateServiceSocket("inproc://service2"))
            using (var csck1 = ctx.CreateClientSocket("inproc://service1"))
            using (var csck2 = ctx.CreateClientSocket("inproc://service2"))
            {
                csck1.Send("hello");

                Assert.IsFalse(NzmqCoordination.WaitAll(100, ssck1, ssck2));

                csck2.Send("world");

                Assert.IsTrue(NzmqCoordination.WaitAll(100, ssck1, ssck2));
            }
        }

        
        [Test]
        public void Wait_for_message_on_any_of_several_sockets()
        {
            using (var ctx = new NzmqContext())
            using (var ssck1 = ctx.CreateServiceSocket("inproc://service1"))
            using (var ssck2 = ctx.CreateServiceSocket("inproc://service2"))
            using (var csck1 = ctx.CreateClientSocket("inproc://service1"))
            using (var csck2 = ctx.CreateClientSocket("inproc://service2"))
            {
                csck1.Send("hello");

                Assert.AreEqual(0, NzmqCoordination.WaitAny(100, ssck1, ssck2)[0]);
                ssck1.Receive();

                csck2.Send("world");

                Assert.AreEqual(1, NzmqCoordination.WaitAny(100, ssck1, ssck2)[0]);
            }
        }
    }
}